package main

import (
	"flag"
	"fmt"
	"gitee.com/quant1x/data/cache"
	"gitee.com/quant1x/data/category"
	"gitee.com/quant1x/data/category/trading"
	"gitee.com/quant1x/data/internal/dfcf"
	"gitee.com/quant1x/data/internal/runtimes"
	"gitee.com/quant1x/data/internal/tdx"
	"gitee.com/quant1x/gotdx/quotes"
	"gitee.com/quant1x/pandas"
	"gitee.com/quant1x/pandas/stat"
	"github.com/mymmsc/gox/cron"
	"github.com/mymmsc/gox/logger"
	"github.com/mymmsc/gox/progressbar"
	"github.com/mymmsc/gox/signal"
	"github.com/mymmsc/gox/util/treemap"
	"os"
	"sort"
	"strconv"
	"sync"
	"time"
)

var (
	MinVersion   string // 版本号
	mapFreeGuBen = map[string]float64{}
)

// 更新日线数据工具
func main() {
	mainStart := time.Now()
	defer func() {
		elapsedTime := time.Since(mainStart) / time.Millisecond
		fmt.Printf("\n总耗时: %.3fs\n", float64(elapsedTime)/1000)
	}()

	//创建监听退出chan
	c := signal.Notify()
	var (
		cronConfig string // 定时脚本
		cronTrue   bool   // 是否启用应用内定时器
		version    bool   // 显示版本号
	)
	flag.StringVar(&cronConfig, "cron_config", "0 0 17 * * ?", "pull code data cron")
	flag.BoolVar(&cronTrue, "cron_true", false, "use crontab in application")
	flag.BoolVar(&version, "version", false, "print version")
	flag.Parse()

	if version {
		fmt.Println(MinVersion)
		os.Exit(0)
	}
	runtimes.GOMAXPROCS()
	if !cronTrue {
		// 1.先跑流通股东数据缓存
		handleHolderData()
		// 2. 基础信息增加自由流通股数
		handleBasicData()
	} else {
		crontab := cron.New(cron.WithSeconds()) //精确到秒
		// 添加定时任务,
		_, _ = crontab.AddFunc(cronConfig, handleBasicData)
		//启动定时器
		crontab.Start()
		select {
		case sig := <-c:
			{
				logger.Info("进程结束:", sig)
				os.Exit(1)
			}
		}
	}
}

func handleBasicData() {
	logger.Info("任务开始启动...")
	tdx.ReOpen()

	df := tdx.GetF10List()
	count := df.Nrow()
	fmt.Println()
	bar := progressbar.NewBar(3, "执行[更新基础信息]", count)
	var wg = sync.WaitGroup{}
	var mapStock *treemap.Map
	basicDate := uint32(stat.AnyToInt64(category.MARKET_CN_FIRST_DATE))
	mapStock = treemap.NewWithStringComparator()
	for i := 0; i < df.Nrow(); i++ {
		m := df.IndexOf(i)
		stockInfo := cache.Security{}
		if v, ok := m["Code"]; ok {
			stockInfo.Code = stat.AnyToString(v)
		}
		if v, ok := m["VolUnit"]; ok {
			stockInfo.VolUnit = int(stat.AnyToInt32(v))
		}
		if v, ok := m["DecimalPoint"]; ok {
			stockInfo.DecimalPoint = int(stat.AnyToInt32(v))
		}
		if v, ok := m["Name"]; ok {
			stockInfo.Name = stat.AnyToString(v)
		}
		if v, ok := m["PreClose"]; ok {
			stockInfo.PreClose = stat.AnyToFloat64(v)
		}

		wg.Add(1)
		pullData := func(pbar *progressbar.Bar, waitGroup *sync.WaitGroup, securityInfo cache.Security, result *treemap.Map) {
			defer waitGroup.Done()
			for i := 0; i < quotes.DefaultRetryTimes; i++ {
				info, err := tdx.F10(securityInfo.Code)
				if err != nil {
					panic(err)
				}
				if info != nil {
					if info.LiuTongGuBen > 0 && info.ZongGuBen > 0 {
						securityInfo.LiuTongGB = info.LiuTongGuBen
						securityInfo.ZongGB = info.ZongGuBen

						if info.IPODate >= basicDate {
							securityInfo.IpoDate = strconv.FormatInt(int64(info.IPODate), 10)
							securityInfo.IpoDate = trading.FixTradeDate(securityInfo.IpoDate)
						}
					}
					if gbFree, ok := mapFreeGuBen[securityInfo.Code]; ok {
						securityInfo.FreeGB = gbFree
					} else {
						securityInfo.FreeGB = securityInfo.LiuTongGB
					}
					result.Put(securityInfo.Code, securityInfo)
					break
				} else {
					time.Sleep(time.Millisecond * 10)
				}
			}
			pbar.Add(1)
		}
		go pullData(bar, &wg, stockInfo, mapStock)
	}
	wg.Wait()
	infos := []cache.Security{}
	mapStock.Each(func(key interface{}, value interface{}) {
		row := value.(cache.Security)
		infos = append(infos, row)
	})
	df1 := pandas.LoadStructs(infos)
	f10Filname := cache.FinanceInfoFilename()
	_ = df1.WriteCSV(f10Filname)
	logger.Info("任务执行完毕.", time.Now())
}

// 十大流通股股东
func handleHolderData() {
	logger.Info("任务开始启动...")
	// 十大流通股股东
	_, pages, _ := dfcf.FreeHoldingAnalyse()
	if pages < 1 {
		return
	}
	//tdx.ReOpen()
	bar := progressbar.NewBar(1, "执行[更新十大流通股东信息]", pages)
	raws := []dfcf.HoldingAnalyse{}
	for pageNo := 1; pageNo < pages+1; pageNo++ {
		bar.Add(1)
		list, pages, err := dfcf.FreeHoldingAnalyse(pageNo)
		if err != nil || pages < 1 {
			logger.Error(err)
			break
		}
		count := len(list)
		if count == 0 {
			break
		}
		raws = append(raws, list...)
		if count < dfcf.EastmoneyGdfxHoldingAnalysePageSize {
			break
		}
	}
	sort.Slice(raws, func(i, j int) bool {
		a := raws[i]
		b := raws[j]
		fcA := a.SECUCODE
		fcB := b.SECUCODE
		if fcA < fcB {
			return true
		}
		tmA := a.END_DATE
		tmB := b.END_DATE

		return fcA == fcB && tmA > tmB

	})
	fmt.Println()
	bar = progressbar.NewBar(2, "执行[流通股东排序]", len(raws))
	holds := []dfcf.HoldingAnalyse{}
	tmp := []dfcf.HoldingAnalyse{}
	fullCode := ""
	date := ""
	for _, v := range raws {
		bar.Add(1)
		_, mname, code := category.DetectMarket(v.SECUCODE)
		tmpCode := mname + code
		v.SECUCODE = tmpCode
		tmpDate := v.END_DATE
		v.END_DATE = tmpDate
		if _, ok := mapFreeGuBen[tmpCode]; ok {
			continue
		}
		if fullCode == "" {
			fullCode = tmpCode
		}
		if date == "" {
			date = tmpDate
		}
		if fullCode == tmpCode && date == tmpDate {
			// 代码和日期相同, 追加
			tmp = append(tmp, v)
		} else {
			// 代码和日期, 其一不相同, 处理结果
			sort.Slice(tmp, func(i, j int) bool {
				a := tmp[i]
				b := tmp[j]
				condition := a.FREE_HOLDNUM_RATIO > b.FREE_HOLDNUM_RATIO
				return condition
			})
			holds = append(holds, tmp...)
			code := tmp[0].SECUCODE
			compute := func(holderList []dfcf.HoldingAnalyse, liuTongGuBen float64) float64 {
				for k, holder := range holderList {
					if k >= 5 {
						break
					}
					if holder.FREE_HOLDNUM_RATIO >= 1.00 || holder.HOLDER_TYPE == "其它" {
						liuTongGuBen -= float64(holder.HOLD_NUM)
					}
				}
				return liuTongGuBen
			}
			gbLiutong := cache.GetLiuTongPan(code)
			gbFree := float64(0)
			if gbLiutong > 0 {
				gbFree = compute(tmp, gbLiutong)
				if gbFree < 0 {
					gbZong := cache.GetZongGuben(code)
					gbFree = compute(tmp, gbZong)
				}
			}
			// 缓存自由流通股本
			mapFreeGuBen[code] = gbFree
			tmp = []dfcf.HoldingAnalyse{}
			// 如果代码不同, 则追加到临时数组
			if v.SECUCODE != code {
				tmp = append(tmp, v)
				fullCode = v.SECUCODE
				date = v.END_DATE
			} else {
				fullCode = ""
				date = ""
			}
		}
	}

	fn := cache.HoldingFilename()
	df := pandas.LoadStructs(holds)
	_ = df.WriteCSV(fn)
	logger.Info("任务执行完毕.", time.Now())
}
